命名函数对象，也称为函子，允许在定义接口的同时操作任意数据集合。当用于内核时，已命名函数对象的成员变量定义了内核可能操作的状态，重载函数调用operator()将为并行执行的每个工作项调用。\par

命名函数对象需要比Lambda更多的代码来表示内核，但提供了更多的功能。例如，更容易分析和优化以命名函数对象表示的内核，因为内核使用的任何缓冲区和数据都必须显式地传递给内核，而不是自动捕获。\par

最后，因为命名函数对象就像C++类一样，表示为命名函数对象的内核可以模板化。以命名函数对象表示的内核也更容易重用，可以作为头文件或库的一部分。\par

\hspace*{\fill} \par %插入空行
\textbf{内核命名函数对象的组成}

图10-6中的代码描述了用命名函数对象表示的内核。\par

\hspace*{\fill} \par %插入空行
图10-6 内核作为命名函数对象
\begin{lstlisting}[caption={}]
class Add {
public:
	Add(accessor<int> acc) : data_acc(acc) {}
	void operator()(id<1> i) {
		data_acc[i] = data_acc[i] + 1;
	}

private:
	accessor<int> data_acc;
};

int main() {
	constexpr size_t size = 16;
	std::array<int, size> data;
	
	for (int i = 0; i < size; i++)
		data[i] = i;
		
	{
		buffer data_buf{data};
		
		queue Q{ host_selector{} };
		std::cout << "Running on device: "
				  << Q.get_device().get_info<info::device::name>() << "\n";
				  
		Q.submit([&](handler& h) {
			accessor data_acc {data_buf, h};
			h.parallel_for(size, Add(data_acc));
		});
	}
});
\end{lstlisting}

当内核表示为命名函数时，必须遵循C++11规则才能复制。命名函数对象可以安全地逐字节地复制，使命名函数对象的成员变量能够传递给在设备上执行的内核代码。\par

重载函数调用operator()的参数取决于内核，就像用Lambda表示的内核一样。\par

因为是命名的函数对象，所以主机代码编译器可以使用函数对象类型与设备代码编译器生成的内核代码相关联。因此，命名内核函数对象不需要内核名称。\par












